/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   World class.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define WORLD_WORLD_CC 1
#define NEED_PLANET_CLASS 1
#if VERBOSE
#define WORLD_VERBOSE 1
#endif
#include "base/module.hh"
#include "base/conf.hh"
#include "base/stream.hh"
using namespace base;
#include "math/module.hh"
#include "math/matrix.hh"
#include "math/matrix_funcs.hh"
#include "math/funcs_trig.hh"
using namespace math;
#include "object/module.hh"
using namespace object;
#include "graph/module.hh"
#include "graph/scene_graph.hh"
using namespace graph;
#include "view/module.hh"
using namespace view;
#include "world/module.hh"
#include "world/defs.hh"
#include "world/conf.hh"
#include "world/planet.hh"

namespace world {

////////////////////////////////////////////////////////////////////////////////
//////////////////////////////  World::Make  ///////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

DEFINE_SINGLETON_CLASS_VARS( World )

/*****************************************************************************
 * Normally called once but can be called again for reloading scene-graph.
 * Called by world::Init() [world/module.cc].
 *****************************************************************************/
void
World::Make( void )
{
    // Create Planet and attach it to scene-graph.
    // But don't re-attach it.
    shptr<Object> planet = GET_PLANET().GetRootObject();
    if ( not GET_SCENE_GRAPH().IfObjectAttached( planet ) )
    {
#if COMPILE_PALOMINO == 3
        const NodeSort nodeSort( WORLD_CONF.mFlatWorld ? NodeSort() : NodeSort( NodeSort::Attribs( NodeSort::Attribs::NO_SHADER ) ) );
#else
        const NodeSort nodeSort;
#endif
        GET_SCENE_GRAPH().AttachObject( planet, nodeSort );

        #if WORLD_VERBOSE
        CLOG << "World::Make: attached planet to scene-graph" << endl;
        #endif
    }
    else
    {
        #if WORLD_VERBOSE
        CLOG << "World::Make: planet already attached to scene-graph" << endl;
        #endif
    }

    ASSERT( GET_SCENE_GRAPH().IfObjectAttached( planet ) );
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////  World : info  ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Get radius of World/Planet.
 *****************************************************************************/
fp
World::GetRadius( void )
{
    return GET_PLANET().RadiusGraphical();
}

/*****************************************************************************
 * @return Volume of world in 3D space.
 *****************************************************************************/
BoxVolume
World::GetVolume( void )
{
    return GET_PLANET().GetVolume();
}

/*****************************************************************************
 * @return Name of world.
 *****************************************************************************/
string
World::GetName( void )
{
    if ( IfFlat() )
        return "flat";  // DO NOT CHANGE, replay data files were recorded that contain this string
    else
        return "ossim";  // DO NOT CHANGE, replay data files were recorded that contain this string
}

/*****************************************************************************
 * If the World is flat ;-)
 * This is queried since FlatPlanet is deficient and some configuration
 * is only applicable to FlatPlanet.
 *****************************************************************************/
bool
World::IfFlat( void )
{
    return GET_PLANET().IfFlat();
}

/*****************************************************************************
 * @return True if position is approx at low altitude.
 *****************************************************************************/
bool
World::IfLowAltitude( const WorldVertex& pos )
{
    return world::conv::WorldVertex2SphereVertex(pos).mAlt < WORLD_CONF.mFlatModelMeterLowAlt;
}

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////  World orientation  //////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * Compute a normalized "up vector" extending from a point on the world.
 * A "down vector" can be computed by multipling by -1.0f.
 *****************************************************************************/
WorldVertex
World::ComputeUpVector( const SphereVertex& sv )
{
    return GET_PLANET().ComputeUpVector( sv );
}

/*****************************************************************************
 * Compute an "up vector" where a 3D vector is at.
 *****************************************************************************/
WorldVertex
World::ComputeUpVector( const WorldVertex& pos )
{
    return ComputeUpVector( world::conv::WorldVertex2SphereVertex( pos ) );
}

/*****************************************************************************
 * Orient an Object to be tangent to a point on the world (in case world is spherical).
 *****************************************************************************/
void
World::RotateTangential( Object& object, const SphereVertex& sv )
{
    if ( not GET_PLANET().IfFlat() )
    {
        Matrix m = GetTangentMatrix( sv );
        m.setTrans( object.GetPosition() );  // keep Object's position
        MatrixRotateLocal( m, XX, Deg2Rad(90.0f) );  // otherwise Object points up (normal to surface)
        object.SetMatrix( m );
    }
    // else flat so NOP
}

/*****************************************************************************
 * Compute a matrix that is tangent to a point on the world.
 *****************************************************************************/
const Matrix&
World::GetTangentMatrix( const SphereVertex& sv )
{
    return GET_PLANET().GetTangentMatrix( sv );
}

} // namespace world
